% \iffalse meta-comment
% !TEX program  = XeLaTeX
%<*internal>
\iffalse
%</internal>
%<*readme>
Introduction
------------
xpinyin is a LaTeX package written to simplify the input of Hanyu Pinyin.
The package provides macros to automatically add pinyin to Chinese characters.
It can only be used in conjunction with xeCJK or CJK/CJKutf8 package.

Given that the implementation of pinyin package of CJK bundle is not very
well, it seems that xpinyin is a good replacement of it.

You can read the package manual (in Chinese) for more detailed explanations.

It may be distributed and/or modified under the conditions of the
LaTeX Project Public License (LPPL), either version 1.3c of this license or
(at your option) any later version. The latest version of this license is in

   http://www.latex-project.org/lppl.txt

and version 1.3 or later is part of all distributions of LaTeX version
2005/12/01 or later.

This work has the LPPL maintenance status "maintained".
The Current Maintainer of this work is Qing Lee.

This package consists of the file  xpinyin.dtx,
             and the derived files xpinyin.sty,
                                   xpinyin.pdf,
                                   xpinyin.ins,
                                   xpinyin.lua,
                                   xpinyin.db,
                                   xpinyin-database.def and
                                   README (this file).

Author
------
Qing Lee
Email: sobenlee@gmail.com

If you are interested in the process of development you may observe

    http://code.google.com/p/ctex-kit/

Installation
------------
The package is supplied in dtx format and as a pre-extracted zip file,
xpinyin.tds.zip. The later is most convenient for most users: simply
unzip this in your local texmf directory and run texhash to update the
database of file locations. If you want to unpack the dtx yourself,
running "luatex xpinyin.dtx" will extract the package whereas
"xelatex xpinyin.dtx" will typeset the documentation.

The package requires LaTeX3 support as provided in the l3kernel and l3packages
bundles. Both of these are available on CTAN as ready-to-install zip files.
Suitable versions are available in the latest version of MiKTeX and TeX Live
(updating the relevant packages online may be necessary).

To compile the documentation without error, you will need the xeCJK package
and some specific Chinese Simplified fonts (TrueType or OpenType).
%</readme>
%<*internal>
\fi
\begingroup
  \def\temp{LaTeX2e}
\expandafter\endgroup\ifx\temp\fmtname\else
\csname fi\endcsname
%</internal>
%<*install>

\begingroup\expandafter\expandafter\expandafter\endgroup
\expandafter\ifx\csname directlua\endcsname\relax
  \begingroup
  \newlinechar=10 %
  \errmessage{^^J
    ********************************************^^J
    * LuaTeX is required to unpack this package.^^J
    ********************************************^^J
  }
  \endgroup
  \csname @@end\expandafter\endcsname\expandafter\end
\fi

\input l3docstrip.tex
\keepsilent
\askforoverwritefalse

\let\MetaPrefix\relax

\preamble

   Copyright (C) 2012-2014 by Qing Lee <sobenlee@gmail.com>
--------------------------------------------------------------------------
   This work may be distributed and/or modified under the
   conditions of the LaTeX Project Public License, either version 1.3
   of this license or (at your option) any later version.
   The latest version of this license is in
     http://www.latex-project.org/lppl.txt
   and version 1.3 or later is part of all distributions of LaTeX
   version 2005/12/01 or later.

   This work has the LPPL maintenance status "maintained".
   The Current Maintainer of this work is Qing Lee.

\endpreamble

\postamble

   This package consists of the file  xpinyin.dtx,
                and the derived files xpinyin.sty,
                                      xpinyin.pdf,
                                      xpinyin.ins,
                                      xpinyin.lua,
                                      xpinyin.db,
                                      xpinyin-database.def and
                                      README.
\endpostamble

\declarepostamble\emptypostamble
\endpostamble

\def\MetaPrefix{-- }

\generate
  {
    \usedir{source/latex/xpinyin}
    \usepostamble\emptypostamble
    \file{xpinyin.lua} {\from{\jobname.dtx}{lua}}
  }

\directlua{dofile("xpinyin.lua")}

\let\MetaPrefix\DoubleperCent

\generate
  {
    \usedir{source/latex/xpinyin}
    \file{xpinyin.ins} {\from{\jobname.dtx}{install}}
    \usedir{tex/latex/xpinyin}
    \file{xpinyin.sty} {\from{\jobname.dtx}{package}}
    \usepostamble\emptypostamble
    \file{xpinyin-database.def}
      {
        \from{\jobname.dtx} {database}
        \from{xpinyin.db}   {}
      }
    \nopreamble\nopostamble
    \usedir{doc/latex/xpinyin}
    \file{README.txt}  {\from{\jobname.dtx}{readme}}
  }

\endbatchfile
%</install>
%<*internal>
\fi
%</internal>
%
%<*driver|package>
\NeedsTeXFormat{LaTeX2e}
\RequirePackage{expl3}
\GetIdInfo$Id: xpinyin.dtx 749 2014-12-24 15:05:15Z sobenlee@gmail.com $
  {Automatically add pinyin to Chinese characters}
%<*driver>
\ProvidesExplFile{\ExplFileName.\ExplFileExtension}
%</driver>
%<package>\ProvidesExplPackage{\ExplFileName}
  {\ExplFileDate}{2.1}{\ExplFileDescription}
%<*driver>
\ExplSyntaxOff
%</driver>
%</driver|package>
%<database>\ProvidesFile{xpinyin-database.def}%
%<database>  [2014/12/24 v2.1 xpinyin database]
%
%<*driver>
\documentclass[full,a4paper]{l3doc}
\usepackage{xeCJK}
\usepackage{indentfirst}
\usepackage{xpinyin}
\usepackage{fvrb-ex}
\usepackage{geometry}
\hypersetup{pdfstartview=FitH}
\geometry{includemp,hmargin={0mm,15mm},vmargin=15mm,footskip=7mm}
\BeforeBeginEnvironment{SideBySideExample}{\vskip1ex\relax}
\fvset{formatcom=\xeCJKVerbAddon}
\linespread{1.1}
\setmainfont{TeX Gyre Pagella}
\setsansfont{CMU Sans Serif}
\setmonofont[
  UprightFont=* Light, BoldFont=* Bold,
  SlantedFont=* Light Oblique]{CMU Typewriter Text}
\setCJKmainfont[BoldFont=Adobe Heiti Std,ItalicFont=Adobe Kaiti Std]{Adobe Song Std}
\setCJKmonofont{Adobe Kaiti Std}
\xeCJKsetup{PunctStyle=kaiming}
\newfontfamily\PinYinFont{TeX Gyre Adventor}
\xpinyinsetup{font=\PinYinFont,multiple=\color{red}}
\def\MacroFont{\linespread{1}\small\normalfont\ttfamily}
\makeatletter
\ExplSyntaxOn
\DeclareDocumentCommand \package { o m }
  {
    \exp_args:Nx \tex_lowercase:D
      {
        \exp_not:N \href
          { http \token_to_str:N : //www.ctan.org/pkg/ \IfNoValueTF {#1} {#2} {#1} }
      } { \pkg {#2} }
  }
\cs_set_protected:Npn \__codedoc_special_index_aux:nnnnn #1#2#3#4#5
  {
    \__codedoc_special_index_set:Nn \l__codedoc_index_escaped_macro_tl {#2}
    \str_if_eq:onTF { \@currenvir } { macrocode }
      { \codeline@wrindex }
      {
        \HD@target
        \index
      }
      {
        \tl_if_empty:nF { #3 #4 }
          { #3 \actualchar #4 \levelchar }
        #1
        \actualchar
        {
          \token_to_str:N \verbatim@font \c_space_tl
          \l__codedoc_index_escaped_macro_tl
        }
        \encapchar
        hdclindex{\the\c@HD@hypercount}{#5}
      }
  }
\DeclareDocumentCommand \cs { O{} m }
  { \__codedoc_cmd_aux:no {#1} { \c__codedoc_backslash_tl #2 } }
\DeclareDocumentCommand \tn { O{} m }
  {
    \__codedoc_cmd_aux:no
      { index = TeX , replace = false , #1 }
      { \c__codedoc_backslash_tl #2 }
  }
\DeclareDocumentCommand \meta { +m }
  { \__codedoc_meta_aux:n {#1} }
\DeclareExpandableDocumentCommand \bookmarkcstn { O{} m }
  { \tl_to_str:n {#2} }
\cs_new:Npn \bookmarkmeta #1 { < \tl_to_str:n {#1} > }
\cs_generate_variant:Nn \__codedoc_cmd_aux:nn { no }
\ExplSyntaxOff
\pdfstringdefDisableCommands{%
  \let\cs\bookmarkcstn
  \let\tn\bookmarkcstn
  \let\meta\bookmarkmeta
  \let\path\@firstofone
  \let\pkg\@firstofone}
\def\LaTeX{\hologo{LaTeX}}
\def\pdfLaTeX{\hologo{pdfLaTeX}}
\def\XeLaTeX{\hologo{XeLaTeX}}
\def\TF{true\orval{}false}
\def\TTF{\defaultval{true}\orval{}false}
\def\TFF{true\orval\defaultval{false}}
\def\orval{\textup{\textbar}}
\def\defaultval#1{\textbf{\textup{#1}}}
\makeatother
\def\indexname{代码索引}
\IndexPrologue{%
  \section*{\indexname}
  \markboth{\indexname}{\indexname}
  斜体的数字表示对应项说明所在的页码，下划线的数字表示定义所在的代码行号，而直立体的
  数字表示对应项使用时所在的行号。}
\begin{document}
  \DocInput{\jobname.dtx}
  \newgeometry{margin=15mm,footskip=7mm}
  \PrintIndex
\end{document}
%</driver>
% \fi
%
% \CheckSum{707}
% \GetFileInfo{\jobname.dtx}
%
% \title{\bfseries\pkg{xpinyin} 宏包}
% \author{李清\\ \path{sobenlee@gmail.com}}
% \date{\filedate\qquad\fileversion}
% \maketitle
%
% \begin{documentation}
%
% \section{简介}
%
% \pkg{xpinyin} 是一个 \LaTeX 宏包，提供了为汉字自动注音的功能。
%
% \section{基本用法}
%
% \pkg{xpinyin} 支持采用 \texttt{GBK} 和 \texttt{UTF-8} 编码的 \TeX 源文件，建议
% 总是使用 \texttt{UTF-8}。如果使用 \LaTeX 或 \pdfLaTeX\ 的编译方式，则根据编码的
% 情况，\pkg{xpinyin} 依赖 \package{CJK} 或者 \package[cjk]{CJKutf8} 宏包。
% 如果使用 \XeLaTeX，则依赖 \package{xeCJK} 宏包。如果它们没有在 \pkg{xpinyin}
% 之前被载入，\pkg{xpinyin} 将根据编译方式自动选择，\LaTeX 或 \pdfLaTeX\ 将使用
% \pkg{CJKutf8}。
%
% \pkg{xpinyin} 还依赖 \package{l3kernel} 和 \package{l3packages}，使用 (pdf)\LaTeX
% 下的 \texttt{GBK} 编码时，还将依赖 \package{xCJK2uni}。
%
% 需要注意的是，\pkg{xpinyin} 缺省将拼音的字体设置为与文档的主字体（\tn{normalfont}）相同，
% 所以为了保证声调字母的正确输出，应该选用合适的西文主字体。也可以通过将在下一节介绍的
% \meta{font} 选项来单独设置拼音的字体。
%
% \begin{minipage}[t]{\dimexpr(\linewidth-\parindent-2em)/2\relax}
% \XeLaTeX 下的简单示例：
% \begin{verbatim}[frame=single,gobble=3]
%   \documentclass{article}
%   \usepackage{xeCJK}
%   \usepackage{xpinyin}
%   \setmainfont{CMU Serif}
%   \setCJKmainfont{SimSun}
%
%   \begin{document}
%   \xpinyin*{汉语拼音示例}
%   \end{document}
% \end{verbatim}
% \end{minipage}\qquad
% \begin{minipage}[t]{\dimexpr(\linewidth-\parindent-2em)/2\relax}
% (pdf)\LaTeX 下的简单示例：
% \begin{verbatim}[frame=single,gobble=3]
%   \documentclass{article}
%   \usepackage{CJKutf8}
%   \usepackage{xpinyin}
%   \usepackage[T1]{fontenc}
%   \usepackage{lmodern}
%
%   \begin{document}
%   \begin{CJK}{UTF8}{gbsn}
%   \xpinyin*{汉语拼音示例}
%   \end{CJK}
%   \end{document}
% \end{verbatim}
% \hrule height \dp\strutbox width 0pt \relax
% \end{minipage}
%
% 运行上述示例要求系统安装了设置的字体，源文件用 |UTF-8| 编码保存，使用相应的编译方式。
%
% \pkg{xpinyin} 可以与 \package{ctex} 宏包或文档类共同使用，使用方式与上面类似。
%
% \section{用户手册}
%
% \begin{function}{pinyinscope}
%   \begin{syntax}
%     \tn{begin}\{pinyinscope\}\oarg{options}
%     .....
%     \tn{end}\{pinyinscope\}
%   \end{syntax}
%   为 \env{pinyinscope} 环境中的汉字自动注音。例如
%   \begin{Example}[frame=single,numbers=left,gobble=5]
%     \begin{pinyinscope}
%     列位看官：你道此书从何而来？说起根由，虽近荒唐，细按则深有趣味。
%     待在下将此来历注明，方使阅者\xpinyin{了}{liao3}然不惑。
%     \end{pinyinscope}
%   \end{Example}
% \end{function}
%
%   可选项 \meta{options} 用于局部设置拼音的格式，将在下面说明。
%
% \begin{function}{\xpinyin}
%   \begin{syntax}
%     \tn{xpinyin} \oarg{options} \Arg{单个汉字} \Arg{拼音}
%     \tn{xpinyin*} \oarg{options} \Arg{文字}
%   \end{syntax}
%   对于多音字，可以使用 \tn{xpinyin} 为其设置拼音；而 \tn{xpinyin*} 相当于
%   \env{pinyinscope} 环境的命令形式。\tn{xpinyin} 可以在 \env{pinyinscope} 环境和
%   \tn{xpinyin*} 中使用。例如，
%   \begin{SideBySideExample}[frame=single,numbers=left,xrightmargin=.5\linewidth,gobble=5]
%     \xpinyin{长}{chang2}\\
%     \xpinyin*{甄士隐梦幻识通灵}\\
%     \xpinyin*{\xpinyin{重}{zhong4}要}
%   \end{SideBySideExample}
% \end{function}
%
% \begin{function}{\pinyin}
%   \begin{syntax}
%     \tn{pinyin} \oarg{options} \Arg{拼音}
%   \end{syntax}
%   用于输出拼音，为了输入的方便 \texttt{\"u} 可以用 |v| 代替。例如，
%   \begin{SideBySideExample}[frame=single,numbers=left,xrightmargin=.5\linewidth,gobble=5]
%     \pinyin{lv2zi}\\
%     \pinyin{nv3hai2zi}
%   \end{SideBySideExample}
% \end{function}
%
% \begin{function}{\setpinyin}
%   \begin{syntax}
%     \tn{setpinyin} \Arg{汉字} \Arg{拼音}
%   \end{syntax}
%   \pkg{xpinyin} 宏包的拼音数据（\file{xpinyin-database.def}）来源于 \texttt{Unicode}
%   的 \texttt{Unihan} 数据库\footnotemark 中的 \file{Unihan_Readings.txt} 文件。对于多
%   音字，一般来说这个文件选用的是常用读音。可以使用 \tn{setpinyin} 来设置多音字的首选读音。
% \end{function}
%
% \footnotetext{\url{http://www.unicode.org/Public/UNIDATA/Unihan.zip}}
%
% \begin{function}{\xpinyinsetup}
%   \begin{syntax}
%     \tn{xpinyinsetup} \{ \meta{key_1}=\meta{val_1}, \meta{key_2}=\meta{val_2}, ... \}
%   \end{syntax}
%   用于在导言区或文档中，设置拼音的格式。目前可以设置的 \meta{key} 如下介绍。
% \end{function}
%
% \begin{function}{ratio}
%   \begin{syntax}
%     ratio = \Arg{number}
%   \end{syntax}
%   设置拼音字体大小与当前正文字体大小的比例，缺省值是 |0.4|。
% \end{function}
%
% \begin{function}{vsep}
%   \begin{syntax}
%     vsep = \Arg{dimen}
%   \end{syntax}
%   设置拼音的基线与汉字基线的间距，缺省值是 |1 em|。
% \end{function}
%
% \begin{function}{hsep}
%   \begin{syntax}
%     hsep = \Arg{skip}
%   \end{syntax}
%   设置注音汉字之间的间距，缺省值与 \tn{CJKglue} 的值相同。为了断行时行末的对齐，设置
%   的 \meta{skip} 最后有一定的弹性。例如
%   \begin{Example}[frame=single,numbers=left,gobble=5]
%     \xpinyin*[ratio={.7},hsep={.5em plus .1em},vsep={1.1em}]{贾雨村风尘怀闺秀}
%   \end{Example}
% \end{function}
%
% \begin{function}{pysep}
%   \begin{syntax}
%     pysep = \Arg{glue}
%   \end{syntax}
%   设置 \tn{pinyin} 输出的相邻两个汉语拼音的空白，缺省值是一个空格。
% \end{function}
%
% \begin{function}{font}
%   \begin{syntax}
%     font = \Arg{font}
%   \end{syntax}
%   设置拼音的字体，缺省值是 \tn{normalfont}，即以正文西文字体相同。为了保证拼音能正确
%   输出，最好选用收字量较大的西文字体。
% \end{function}
%
% \begin{function}{format}
%   \begin{syntax}
%     format = \Arg{format}
%   \end{syntax}
%   设置拼音的其它格式，例如颜色等，缺省值为空。
% \end{function}
%
% \begin{function}{multiple}
%   \begin{syntax}
%     multiple = \Arg{format}
%   \end{syntax}
%   设置多音字拼音的其它格式，缺省值为空。可以通过这个选项来提醒校正多音字的拼音。例如
%   本文档设置多音字拼音的颜色是红色（需要载入 \pkg{color} 宏包）：
%   \begin{verbatim}[frame=single,gobble=5]
%     \xpinyinsetup{multiple={\color{red}}}
%   \end{verbatim}
% \end{function}
%
% \begin{function}[added=2014/01/12]{footnote}
%   \begin{syntax}
%     footnote = \meta{\TFF}
%   \end{syntax}
%   是否对拼音环境中的脚注（\tn{footnote}）汉字加上拼音。缺省值为 \texttt{false}。
%   更一般的情况，请使用 \tn{disablepinyin}。
% \end{function}
%
% \begin{function}[added=2014/01/12]{\disablepinyin,\enablepinyin}
%   \tn{disablepinyin} 用于在拼音环境（\env{pinyinscope}）中临时取消对汉字的注音，而
%   \tn{enablepinyin} 用于其后的恢复。
% \end{function}
%
% \end{documentation}
%
% \StopEventually{}
%
% \begin{implementation}
%
% \section{代码实现}
%
%    \begin{macrocode}
%<*package>
%<@@=xpinyin>
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnn  { xpinyin } { no-LuaTeX }
  {
    The~xpinyin~package~is~not~supported~in~LuaTeX.\\\\
    You~must~change~your~typesetting~engine~to\\
    "xelatex"~or~"pdflatex"~or~"latex"~instead~of~"lualatex".
  }
\luatex_if_engine:T { \msg_critical:nn { xpinyin } { no-LuaTeX } }
%    \end{macrocode}
%
%    \begin{macrocode}
\RequirePackage { xparse }
\RequirePackage { l3keys2e }
%    \end{macrocode}
%
% \begin{macro}[internal]{\c_@@_tone_prop}
% 重音标记 |\`|、|\'| 和 |\=| 在 \env{tabbing} 环境中被移作他用，为避免错误，
% 我们使用内部命令 \tn{@tabacckludge} 或 \tn{a} 来定义。
%    \begin{macrocode}
\prop_new:N \c_@@_tone_prop
\clist_map_inline:nn
  {
    { ā }{ \@tabacckludge= a } ,         { á }{ \@tabacckludge' a } ,
    { ǎ }{ \v a } ,                      { à }{ \@tabacckludge` a } ,
    { ō }{ \@tabacckludge= o } ,         { ó }{ \@tabacckludge' o } ,
    { ǒ }{ \v o } ,                      { ò }{ \@tabacckludge` o } ,
    { ē }{ \@tabacckludge= e } ,         { é }{ \@tabacckludge' e } ,
    { ě }{ \v e } ,                      { è }{ \@tabacckludge` e } ,
    { ū }{ \@tabacckludge= u } ,         { ú }{ \@tabacckludge' u } ,
    { ǔ }{ \v u } ,                      { ù }{ \@tabacckludge` u } ,
    { ḿ }{ \@tabacckludge' m } ,         { ń }{ \@tabacckludge' n } ,
    { ň }{ \v n } ,                      { ǹ }{ \@tabacckludge` n } ,
    { ī }{ \@tabacckludge= { \i } } ,    { í }{ \@tabacckludge' { \i } } ,
    { ǐ }{ \v { \i } } ,                 { ì }{ \@tabacckludge` { \i } } ,
    { ü }{ \" u } ,
    { ǖ }{ \@tabacckludge= { \" u } } ,  { ǘ }{ \@tabacckludge' { \" u } } ,
    { ǚ }{ \v { \" u } } ,               { ǜ }{ \@tabacckludge` { \" u } }
  }
  { \prop_gput:Nnn \c_@@_tone_prop #1 }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{\@@_UTF_char:nn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_UTF_char:nn #1#2
  {
    \cs_if_exist:cF { u8:#1 }
      { \tl_const:cn { u8:#1 } {#2} }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[internal]{\@@_GBK_char:nn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_GBK_char:nn #1#2
  {
    \@@_UTF_char:nn {#1} {#2}
    \exp_args:Nx \@@_GBK_char_aux:nn { \tl_head:n {#1} } {#1}
  }
\cs_new_protected_nopar:Npn \@@_GBK_char_aux:nn #1#2
  { \exp_args:Nf \@@_GBK_char_aux:nnn { \int_eval:n { `#1 } } {#1} {#2} }
\cs_new_protected_nopar:Npn \@@_GBK_char_aux:nnn #1#2#3
  {
    \cs_if_exist:cF { @@_UTF_ #1 :w }
      {
        \exp_args:Nf \@@_GBK_char_def:nnn
          {
            \int_case:nn { \tl_count:n {#3} }
              {
                { \c_two   } { ##1 }
                { \c_three } { ##1##2 }
                { \c_four  } { ##1##2##3 }
              }
          }
          {#1} {#2}
        \exp_args:Nc \@@_save_UTF_cs:Nn { @@_UTF_ #1 :w } {#1}
        \tl_gput_right:Nx \c_@@_reset_UTF_catcode_tl
          { \char_set_catcode:nn {#1} { \char_value_catcode:n {#1} } }
        \char_set_catcode_active:n {#1}
      }
  }
\cs_new_protected_nopar:Npn \@@_GBK_char_def:nnn #1#2#3
  {
    \cs_new_protected_nopar:cpn { @@_UTF_ #2 :w } #1
      { \use:c { u8: \tl_to_str:n { #3#1 } } }
  }
\tl_new:N \c_@@_reset_UTF_catcode_tl
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_save_UTF_cs:Nn}
%    \begin{macrocode}
\group_begin:
\char_set_catcode_active:n { 126 }
\cs_new_protected_nopar:Npn \@@_save_UTF_cs:Nn #1#2
  {
    \group_begin:
    \char_set_lccode:nn { 126 } {#2}
    \tex_lowercase:D
      {
        \group_end:
        \tl_gput_right:Nn \c_@@_reset_UTF_cs_tl { \cs_set_eq:NN ~ #1 }
      }
  }
\group_end:
\tl_new:N \c_@@_reset_UTF_cs_tl
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
\bool_new:N \g_@@_GBK_bool
\@ifpackageloaded { xeCJK }
  { \AtEndOfPackage { \@@_adjust_xeCJK_hook: } }
  {
    \@ifpackageloaded { CJKutf8 }
      {
        \prop_map_function:NN \c_@@_tone_prop \@@_UTF_char:nn
        \AtEndOfPackage { \@@_adjust_CJK_hook: }
      }
      {
        \@ifpackageloaded { CJK }
          {
            \RequirePackage { xCJK2uni }
            \prop_map_function:NN \c_@@_tone_prop \@@_GBK_char:nn
            \AtEndOfPackage
              {
                \tl_put_right:Nn \l_@@_pinyin_box_hook_tl
                  { \c_@@_reset_UTF_cs_tl }
                \@@_adjust_CJK_hook:
                \tl_use:N \c_@@_reset_UTF_catcode_tl
              }
            \bool_gset_true:N \g_@@_GBK_bool
          }
          {
            \xetex_if_engine:TF
              {
                \RequirePackage { xeCJK }
                \AtEndOfPackage { \@@_adjust_xeCJK_hook: }
              }
              {
                \RequirePackage { CJKutf8 }
                \prop_map_function:NN \c_@@_tone_prop \@@_UTF_char:nn
                \AtEndOfPackage { \@@_adjust_CJK_hook: }
              }
          }
      }
  }
%    \end{macrocode}
%
% \begin{macro}[internal,var]{\l_@@_tmpa_box,\l_@@_tmpb_box}
%    \begin{macrocode}
\box_new:N \l_@@_tmpa_box
\box_new:N \l_@@_tmpb_box
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_width:Nn}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_width:Nn #1#2
  {
    \hbox_set:Nn \l_@@_tmpa_box {#2}
    #1 = \box_wd:N \l_@@_tmpa_box
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_leave_vmode:}
%    \begin{macrocode}
\cs_if_exist:NTF \quitvmode
  { \cs_new_eq:NN \@@_quit_vmode: \quitvmode }
  {
    \cs_new_protected_nopar:Npn \@@_quit_vmode:
      {
        \if_mode_vertical:
          \exp_after:wN \tex_indent:D
        \fi:
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_make_pinyin_box:nnn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_make_pinyin_box:nnn #1#2#3
  {
    \@@_leavevmode:
    \hbox_overlap_right:n
      {
        \hbox_set:Nn \l_@@_tmpa_box
          { \@@_CJKsymbol_hook: \@@_save_CJKsymbol:n {#2} }
        \hbox_set:Nn \l_@@_tmpb_box
          {
            \color_group_begin: \color_ensure_current:
            \l_@@_pinyin_box_hook_tl
            \@@_select_font:
            \clist_if_exist:cTF { c_@@_multiple_ #1 _clist }
              { \l_@@_multiple_tl \l_@@_format_tl }
              { \l_@@_format_tl }
            {#3}
            \color_group_end:
          }
        \dim_compare:nNnT
          { \box_wd:N \l_@@_tmpb_box } >
          { \box_wd:N \l_@@_tmpa_box + \l_@@_CJKglue_dim }
          {
            \box_resize:Nnn \l_@@_tmpb_box
              { \box_wd:N \l_@@_tmpa_box + \l_@@_CJKglue_dim }
              { \box_ht:N \l_@@_tmpb_box + \box_dp:N \l_@@_tmpb_box }
          }
        \box_move_up:nn { \l_@@_vsep_tl }
          {
            \hbox_to_wd:nn { \box_wd:N \l_@@_tmpa_box }
              { \tex_hss:D  \box_use_clear:N \l_@@_tmpb_box \tex_hss:D }
          }
      }
  }
\tl_new:N \l_@@_pinyin_box_hook_tl
\pdftex_if_engine:T
  {
    \tl_put_right:Nn \l_@@_pinyin_box_hook_tl
      { \cs_set_eq:NN \CJK@plane \tex_undefined:D }
  }
\cs_generate_variant:Nn \@@_make_pinyin_box:nnn { x }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_CJKsymbol:n}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_CJKsymbol:n #1
  { \@@_CJKsymbol:xn { \@@_to_unicode:n {#1} } {#1} }
\cs_new_protected_nopar:Npn \@@_CJKsymbol:nn #1#2
  {
    \@@_make_pinyin_box:nnn {#1} {#2} { \use:c { c_@@_ #1 _tl } }
    \@@_save_CJKsymbol:n {#2}
  }
\cs_generate_variant:Nn \@@_CJKsymbol:nn { x }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{pinyinscope}
%    \begin{macrocode}
\NewDocumentEnvironment { pinyinscope } { O { } }
  {
    \keys_set:nn { xpinyin } {#1}
    \enablepinyin
  }
  { }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xpinyin}
%    \begin{macrocode}
\NewDocumentCommand \xpinyin { s O { } m }
  {
    \IfBooleanTF {#1}
      {
        \group_begin:
        \keys_set:nn { xpinyin } {#2}
        \enablepinyin
        #3
        \group_end:
      }
      {
        \group_begin:
        \keys_set:nn { xpinyin } {#2}
        \bool_if:NF \l_@@_enable_bool
          { \@@_width:Nn \l_@@_CJKglue_dim { \CJKglue } }
        \@@_quit_vmode:
        \@@_single_aux:nn {#3}
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\l_@@_enable_bool}
%    \begin{macrocode}
\bool_new:N \l_@@_enable_bool
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_CJKglue:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_CJKglue:
  { \skip_horizontal:n { \l_@@_hsep_tl } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\enablepinyin}
%    \begin{macrocode}
\NewDocumentCommand \enablepinyin { }
  {
    \bool_if:NF \l_@@_enable_bool
      {
        \tl_if_empty:NF \l_@@_hsep_tl
          {
            \cs_set_eq:NN \@@_save_CJKglue: \CJKglue
            \cs_set_eq:NN \CJKglue \@@_CJKglue:
          }
        \@@_width:Nn \l_@@_CJKglue_dim { \CJKglue }
        \@@_replace_CJKsymbol:
        \@@_restore_footnote:
        \bool_set_true:N \l_@@_enable_bool
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\disablepinyin}
%    \begin{macrocode}
\NewDocumentCommand \disablepinyin { }
  {
    \bool_if:NT \l_@@_enable_bool
      {
        \cs_if_eq:NNT \CJKglue \@@_CJKglue:
          { \cs_set_eq:NN \CJKglue \@@_save_CJKglue: }
        \@@_restore_CJKsymbol:
        \bool_set_false:N \l_@@_enable_bool
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_restore_footnote:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_restore_footnote:
  {
    \bool_if:NF \l_@@_footnote_bool
      { \tl_put_left:Nn \@parboxrestore { \l_@@_restore_footnote_tl } }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\l_@@_restore_footnote_tl}
%    \begin{macrocode}
\tl_new:N \l_@@_restore_footnote_tl
\tl_set:Nn \l_@@_restore_footnote_tl
  {
    \int_compare:nNnT \etex_currentgrouptype:D = \c_eleven
      { \disablepinyin }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal,var]{\l_@@_CJKglue_dim}
%    \begin{macrocode}
\dim_new:N \l_@@_CJKglue_dim
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_single_aux:nn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_single_aux:nn #1#2
  {
    \@@_replace_CJKsymbol_single:n {#2}
    #1
    \group_end:
  }
\cs_new_protected_nopar:Npn \@@_replace_CJKsymbol_single_aux:n #1
  {
    \bool_if:NF \l_@@_enable_bool { \@@_replace_CJKsymbol: }
    \cs_set_protected_nopar:Npn \CJKsymbol ##1
      { \@@_single_CJKsymbol:nn {##1} {#1} }
  }
\cs_new_protected_nopar:Npn \@@_single_CJKsymbol:nn #1#2
  {
    \@@_make_pinyin_box:xnn
      { \@@_to_unicode:n {#1} } {#1} { \@@_pinyin:n {#2} }
    \@@_save_CJKsymbol:n {#1}
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_replace_CJKsymbol_aux:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_replace_CJKsymbol_aux:
  {
    \cs_set_eq:NN \@@_save_CJKsymbol:n \CJKsymbol
    \cs_set_eq:NN \CJKsymbol \@@_CJKsymbol:n
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_restore_CJKsymbol_aux:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_restore_CJKsymbol_aux:
  { \cs_set_eq:NN \CJKsymbol \@@_save_CJKsymbol:n }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_select_font_xetex:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_select_font_xetex:
  {
    \cs_if_exist_use:cF { \l_@@_coor_tl }
      {
        \tl_set:Nx \l_@@_current_coor_tl { \l_@@_coor_tl }
        \@@_select_font_aux:
        \int_compare:nNnF { \XeTeXfonttype \tex_font:D } = \c_zero
          {
            \exp_last_unbraced:NNV
            \cs_gset_eq:cN \l_@@_current_coor_tl \tex_font:D
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_select_font_aux:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_select_font_aux:
  {
    \fontsize
      { \l_@@_ratio_tl \etex_dimexpr:D \f@size pt \scan_stop: }
      { \f@baselineskip }
    \normalfont
    \l_@@_font_tl
    \selectfont
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_to_unicode_xetex:n}
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_to_unicode_xetex:n #1
  { \int_to_arabic:n { `#1 } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_UTF_to_unicode:n,\@@_UTFchar_to_unicode:n}
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_UTF_to_unicode:n #1
  {
    \int_to_arabic:n
      { \exp_args:No \int_from_hex:n { \CJK@plane } * "100 + #1 }
  }
\cs_new_nopar:Npn \@@_UTFchar_to_unicode:n #1
  { \int_to_arabic:n { \@@_UTF_viii_to_unicode:NNNw #1 \q_stop } }
\cs_new_nopar:Npn \@@_UTF_viii_to_unicode:NNNw #1#2#3#4 \q_stop
  {
    \tl_if_empty:nTF {#4}
      { ( `#1 - "E0 ) * "1000 + ( `#2 - "80 ) * "40 + ( `#3 - "80 ) }
      { ( `#1 - "F0 ) * "4000 + ( `#2 - "80 ) * "1000 + ( `#3 - "80 ) * "40 + ( `#4 - "80 ) }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_GBK_to_unicode:n,\@@_GBKchar_to_unicode:n}
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_GBK_to_unicode:n #1
  { \int_to_arabic:n { " \CJKtu_sfd_map:nn { \CJK@plane } {#1} } }
\cs_new_nopar:Npn \@@_GBKchar_to_unicode:n #1
  { \int_to_arabic:n { " \CJKchartouni {#1} } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_adjust_xeCJK_hook:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_adjust_xeCJK_hook:
  {
    \cs_new_eq:NN \@@_select_font:       \@@_select_font_xetex:
    \cs_new_eq:NN \@@_to_unicode:n       \@@_to_unicode_xetex:n
    \cs_new_eq:NN \@@_char_to_unicode:n  \@@_to_unicode:n
    \cs_new_eq:NN \@@_restore_CJKsymbol: \@@_restore_CJKsymbol_aux:
    \cs_new_eq:NN \@@_replace_CJKsymbol: \@@_replace_CJKsymbol_aux:
    \cs_new_eq:NN \@@_replace_CJKsymbol_single:n
                  \@@_replace_CJKsymbol_single_aux:n
    \tl_if_exist:NTF \l_xeCJK_current_font_tl
      {
        \tl_set:Nn \l_@@_coor_tl
          {
            ( \tl_to_str:N \l_@@_font_tl ) /
            \l_xeCJK_current_font_tl/\l_@@_ratio_tl
          }
      }
      {
        \tl_set:Nn \l_@@_coor_tl
          {
            ( \tl_to_str:N \l_@@_font_tl ) /
            \xeCJK@family/\f@series/\f@shape/\f@size/\l_@@_ratio_tl
          }
      }
    \cs_new_eq:NN \@@_leavevmode: \prg_do_nothing:
    \cs_new_protected_nopar:Npx \@@_CJKsymbol_hook:
      {
        \exp_not:N \makexeCJKinactive
        \cs_if_exist_use:NF \xeCJK_select_font:
          { \exp_not:N \xeCJK@setfont }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_adjust_CJK_hook:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_adjust_CJK_hook:
  {
    \bool_if:NTF \g_@@_GBK_bool
      {
        \cs_new_eq:NN \@@_to_unicode:n      \@@_GBK_to_unicode:n
        \cs_new_eq:NN \@@_char_to_unicode:n \@@_GBKchar_to_unicode:n
      }
      {
        \cs_new_eq:NN \@@_to_unicode:n      \@@_UTF_to_unicode:n
        \cs_new_eq:NN \@@_char_to_unicode:n \@@_UTFchar_to_unicode:n
      }
    \cs_new_eq:NN \@@_select_font:    \@@_select_font_aux:
    \cs_new_eq:NN \@@_leavevmode:     \@@_quit_vmode:
    \cs_new_eq:NN \@@_CJKsymbol_hook: \prg_do_nothing:
    \@ifpackageloaded { CJKpunct }
      { \@@_adjust_CJKpunct_hook: }
      {
        \cs_new_eq:NN \@@_restore_CJKsymbol: \@@_restore_CJKsymbol_aux:
        \cs_new_eq:NN \@@_replace_CJKsymbol: \@@_replace_CJKsymbol_aux:
        \cs_new_eq:NN \@@_replace_CJKsymbol_single:n
                      \@@_replace_CJKsymbol_single_aux:n
        \AtBeginDocument
          {
            \@ifpackageloaded { CJKpunct }
              {
                \cs_undefine:N \@@_restore_CJKsymbol:
                \cs_undefine:N \@@_replace_CJKsymbol:
                \cs_undefine:N \@@_replace_CJKsymbol_single:n
                \@@_adjust_CJKpunct_hook:
              } { }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_adjust_CJKpunct_hook:}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_adjust_CJKpunct_hook:
  {
    \cs_new_protected_nopar:Npn \@@_restore_CJKsymbol:
      {
        \int_compare:nNnTF { \CJKpunct@punctstyle } = { \CJKpunct@ps@plain }
          { \@@_restore_CJKsymbol_aux: }
          { \cs_set_eq:NN \CJKosymbol \@@_save_CJKsymbol:n }
      }
    \cs_new_protected_nopar:Npn \@@_replace_CJKsymbol:
      {
        \int_compare:nNnTF { \CJKpunct@punctstyle } = { \CJKpunct@ps@plain }
          { \@@_replace_CJKsymbol_aux: }
          {
            \cs_set_eq:NN \@@_save_CJKsymbol:n \CJKosymbol
            \cs_set_eq:NN \CJKosymbol \@@_CJKsymbol:n
          }
      }
    \cs_new_protected_nopar:Npn \@@_replace_CJKsymbol_single:n ##1
      {
        \int_compare:nNnTF { \CJKpunct@punctstyle } = { \CJKpunct@ps@plain }
          { \@@_replace_CJKsymbol_single_aux:n { ##1 } }
          {
            \bool_if:NF \l_@@_enable_bool
              { \cs_set_eq:NN \@@_save_CJKsymbol:n \CJKosymbol }
            \cs_set_protected_nopar:Npn \CJKosymbol ####1
              { \@@_single_CJKsymbol:nn { ####1 } { ##1 } }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\pinyin}
%    \begin{macrocode}
\NewDocumentCommand \pinyin { O { } m }
  {
    \group_begin:
    \keys_set:nn { xpinyin } {#1}
    \l_@@_font_tl
    \l_@@_format_tl { }
    \selectfont
    \c_@@_reset_UTF_cs_tl
    \@@_pinyin:n {#2}
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_pinyin:n}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_pinyin:n #1
  {
    \@@_pinyin_init:
    \bool_set_true:N \l_@@_first_bool
    \tl_set:Nn \l_@@_save_tl {#1}
    \@@_pinyin_aux:n #1 \q_recursion_tail \q_recursion_stop
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_pinyin_aux:n}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_pinyin_aux:n #1
  {
    \quark_if_recursion_tail_stop_do:nn {#1}
      {
        \bool_if:NTF \l_@@_first_bool { \l_@@_save_tl }
          { \tl_if_empty:NF \l_@@_item_tl { \l_@@_pysep_tl \l_@@_item_tl } }
      }
    \@@_if_number:nTF {#1}
      {
        \bool_if:NTF \l_@@_first_bool
          { \bool_set_false:N \l_@@_first_bool }
          { \l_@@_pysep_tl }
        \l_@@_pre_tl
        \@@_tone:Vn \l_@@_tone_tl {#1}
        \l_@@_post_tl
        \@@_pinyin_init:
      }
      {
        \int_compare:nNnTF
          { 0 \cs_if_exist_use:c { c_@@_ \tl_to_str:N \l_@@_tone_tl _tl } } >
          { 0 \cs_if_exist_use:c { c_@@_ \tl_to_str:n {#1} _tl } }
          { \tl_put_right:Nn \l_@@_post_tl {#1} }
          {
            \tl_set:Nn \l_@@_tone_tl {#1}
            \tl_set_eq:NN \l_@@_pre_tl \l_@@_item_tl
            \tl_clear:N \l_@@_post_tl
          }
        \tl_put_right:Nx \l_@@_item_tl { \@@_replace_v:n {#1} }
      }
    \@@_pinyin_aux:n
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_tone:Nn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_tone:Nn #1#2
  { \use:c { @@_num_to_tone_ #1 :Nn } {#1} {#2} }
\cs_generate_variant:Nn \@@_tone:Nn { V }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_replace_v:n}
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_replace_v:n #1
  {
    \str_if_eq:nnTF {#1} { v }
      {
        \str_case:onTF { \l_@@_item_tl }
          { { l } { } { n } { } { L } { } { N } { } }
          { \exp_not:n { ü } } { u }
      }
      { \exp_not:n {#1} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_pinyin_init:}
%    \begin{macrocode}
\cs_new_nopar:Npn \@@_pinyin_init:
  {
    \tl_clear:N \l_@@_pre_tl   \tl_clear:N \l_@@_post_tl
    \tl_clear:N \l_@@_item_tl  \tl_clear:N \l_@@_tone_tl
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_if_number:nTF}
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_number:n #1 { TF }
  {
    \if_int_compare:w \c_one < 1 \tl_to_str:n {#1} \exp_stop_f:
      \prg_return_true: \else: \prg_return_false: \fi:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal,var]{\l_@@_first_bool}
%    \begin{macrocode}
\bool_new:N \l_@@_first_bool
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal,var]
% {\c_@@_a_tl,\c_@@_o_tl,\c_@@_e_tl,\c_@@_i_tl,\c_@@_u_tl,\c_@@_v_tl}
%    \begin{macrocode}
\tl_const:Nn \c_@@_a_tl { 3 }
\tl_const:Nn \c_@@_o_tl { 2 }
\tl_const:Nn \c_@@_e_tl { 2 }
\tl_const:Nn \c_@@_i_tl { 1 }
\tl_const:Nn \c_@@_u_tl { 1 }
\tl_const:Nn \c_@@_v_tl { 1 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\@@_num_to_tone:Nn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \@@_num_to_tone:Nn #1#2
  {
    \if_case:w \int_eval:n { #2 - \c_one } \exp_stop_f:
      \= {#1}  \or: \'{#1}  \or: \v {#1}  \or: \` {#1}  \else: #1 \fi:
  }
\tl_map_inline:nn { a o e u }
  { \cs_new_eq:cN { @@_num_to_tone_ #1 :Nn } \@@_num_to_tone:Nn }
\cs_new_nopar:Npn \@@_num_to_tone_i:Nn #1#2
  {
    \if_case:w \int_eval:n { #2 - \c_one } \exp_stop_f:
      ī  \or: í  \or: ǐ  \or: ì \else: i \fi:
  }
\cs_new_protected_nopar:Npn \@@_num_to_tone_v:Nn #1#2
  {
    \str_case:onTF { \l_@@_pre_tl }
      { { l } { }  { n } { }  { L } { } { N } { } }
      {
        \if_case:w \int_eval:n { #2 - \c_one } \exp_stop_f:
          ǖ  \or: ǘ  \or: ǚ  \or: ǜ \else: ü \fi:
      }
      { \@@_num_to_tone:Nn u {#2} }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\xpinyinsetup}
%    \begin{macrocode}
\NewDocumentCommand \xpinyinsetup { m } { \keys_set:nn { xpinyin } {#1} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{ratio,vsep,hsep,pysep,font,format,multiple,footnote}
%    \begin{macrocode}
\clist_map_inline:nn
  { ratio , vsep , hsep , pysep , font , format , multiple }
  { \keys_define:nn { xpinyin } { #1 .tl_set:c = { l_@@_ #1 _tl } } }
\keys_define:nn { xpinyin }
  { footnote .bool_set:N = \l_@@_footnote_bool }
\keys_set:nn { xpinyin }
  {
    ratio   = .4 ,
    vsep    = 1 em ,
    pysep   = \c_space_tl ,
    font    = \normalfont ,
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[internal]{\xpinyin_customary:nnn,\xpinyin_multiple:nnn}
%    \begin{macrocode}
\cs_new_protected_nopar:Npn \xpinyin_customary:nnn #1#2#3
  { \cs_gset_nopar:cpn { c_@@_ #2 _tl } {#3} }
\cs_new_protected_nopar:Npn \xpinyin_multiple:nnn #1#2#3
  { \cs_gset_nopar:cpn { c_@@_multiple_ #2 _clist } {#3} }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
\group_begin:
  \cs_set_eq:NN \XPYU  \xpinyin_customary:nnn
  \cs_set_eq:NN \XPYUM \xpinyin_multiple:nnn
  \file_input:n { xpinyin-database.def }
\group_end:
%    \end{macrocode}
%
% \begin{macro}{\setpinyin}
%    \begin{macrocode}
\NewDocumentCommand \setpinyin { m m }
  {
    \tl_set:cn
      { c_@@_ \@@_char_to_unicode:n {#1} _tl }
      { \@@_pinyin:n {#2} }
  }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
\ProcessKeysOptions { xpinyin }
%    \end{macrocode}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \section{\pkg{xpinyin.lua}}
%
%    \begin{macrocode}
%<*lua>
%    \end{macrocode}
%
%    \begin{macrocode}
xpinyin       = xpinyin or { }
local xpinyin = xpinyin
%    \end{macrocode}
%
% 计算时区\footnote{\url{http://lua-users.org/wiki/TimeZone}}。
%    \begin{macrocode}
xpinyin.tzoffset = "+0000"
do
  -- Compute the difference in seconds between local time and UTC.
  local function get_timezone()
    local now = os.time()
    return os.difftime(now, os.time(os.date("!*t", now)))
  end
  -- Return a timezone string in ISO 8601:2000 standard form (+hhmm or -hhmm)
  local function get_tzoffset(timezone)
    local h, m = math.modf(timezone / 3600)
    return string.format("%+.4d", 100 * h + 60 * m)
  end
  xpinyin.tzoffset = get_tzoffset(get_timezone())
end
%    \end{macrocode}
%
%    \begin{macrocode}
xpinyin = {
  svnid    = "$Id: xpinyin.dtx 749 2014-12-24 15:05:15Z sobenlee@gmail.com $",
  uchar    = unicode.utf8.char,
  readings = { },
  fixreadings = {
%    \end{macrocode}
% 为汉字“〇”增加拼音。
%    \begin{macrocode}
    {"U+3007", "Mandarin", "líng"}
  },
%    \end{macrocode}
%
%    \begin{macrocode}
  database = {
    source  = "http://www.unicode.org/Public/UNIDATA/Unihan.zip",
    file    = "Unihan_Readings.txt",
    date    = "Date: 2014-05-09 18:17:02 GMT [JHJ]",
    version = "Unicode version: 7.0.0",
    dbfile  = "xpinyin.db"
  },
%    \end{macrocode}
% \pkg{DocStrip} 会将一行开头的 |%%| 替换成 \tn{MetaPrefix}，因此我们在行首加了
% 空格，需要把它去掉。
%    \begin{macrocode}
  preamble = string.gsub([[
    %%
    %%  Do not edit this file!
    %%  Created from Unihan database:
    %%
    %%    $file
    %%    $date
    %%    $version
    %%
    %%  by "texlua xpinyin.lua" on ]]
               .. os.date("%Y-%m-%d %X ") .. xpinyin.tzoffset
               .. "\n%%", "[ ]+(%%%%)", "%1")
}
%    \end{macrocode}
%
% 将 \file{Unihan_Readings.txt}^^A
% \footnote{\url{http://http://www.unicode.org/reports/tr38/}。} 保存到一张表里面。
%    \begin{macrocode}
function xpinyin.maketable (txt)
  local f = io.open(txt or xpinyin.database.file, "r")
  if not f then
    local source = xpinyin.database.source
    local zfilename = source:match("[^/]+$")
    local zfile = zip.open(zfilename)
    if not zfile then
      xpinyin.download(source, zfilename)
      zfile = assert(zip.open(zfilename))
    end
    f = assert(zfile:open(xpinyin.database.file))
    zfile:close()
  end
  local s, prop
  for line in f:lines() do
    s = line:explode("\t")
    if #s == 3 then
      prop = s[2]:sub(2)
      if prop == "Mandarin" or
         prop == "HanyuPinyin" or
         prop == "XHC1983" or
         prop == "HanyuPinlu" then
        xpinyin.insert(s[1], prop, s[3])
      end
    elseif line:find("Date") then
      xpinyin.database.date = line:match("^[#%s]*(.*)")
    elseif line:find("Unicode version:") then
      xpinyin.database.version = line:match("^[#%s]*(.*)")
    end
  end
  f:close()
  if xpinyin.fixreadings then
    for _, s in pairs(xpinyin.fixreadings) do
      xpinyin.insert(s[1], s[2], s[3])
    end
  end
end
%    \end{macrocode}
%
% 下载 \file{Unihan.zip}。
%    \begin{macrocode}
function xpinyin.download (source, zip)
  print("\nRetrieving Unihan Database from\n", source)
  local unihan_data = assert(socket.http.request(source), "download failed")
  local f = assert(io.open(zip, "wb"), "Unihan file (" .. zip .. ") not writable")
  f:write(unihan_data)
  f:close()
end
%    \end{macrocode}
%
% 往拼音表中加入项目。
%    \begin{macrocode}
function xpinyin.insert (unicode, prop, value)
  local index = tonumber(unicode:match("%x+$"), 16)
  if not xpinyin.readings[index] then
    xpinyin.readings[index] = { }
  end
  xpinyin.readings[index][prop] = value
end
%    \end{macrocode}
%
% 输出需要的格式文件。
%    \begin{macrocode}
function xpinyin.output (db)
  local f = assert(io.open(db or xpinyin.database.dbfile, "w"))
  local preamble = xpinyin.preamble:gsub("%$(%w+)", xpinyin.database)
  f:write(preamble, "\n")
  local hanzi, pinyin
  local mt = { }
  for index, pyt in xpinyin.pairsByKeys(xpinyin.readings) do
    pinyin = assert(xpinyin.grep(pyt))
    hanzi = xpinyin.uchar(index)
    f:write("\\XPYU{", hanzi, "}{", index, "}{", pinyin, "}\n")
    pinyin = xpinyin.multiple(pyt)
    if pinyin then
      mt[#mt + 1] = "\\XPYUM{" .. hanzi .. "}{" .. index .. "}{" .. pinyin .."}"
    end
  end
  f:write(table.concat(mt, "\n"), "\n")
  f:close()
end
%    \end{macrocode}
%
% 将表按照索引排序，代码来源于
% \href{http://www.lua.org/pil/19.3.html}{\textit{Programming in Lua}}。
%    \begin{macrocode}
function xpinyin.pairsByKeys (t, f)
  local a = { }
  for n in pairs(t) do a[#a + 1] = n end
  table.sort(a, f)
  local i = 0 -- iterator variable
  return function () -- iterator function
    i = i + 1
    return a[i], t[a[i]]
  end
end
%    \end{macrocode}
%
% 按照 \texttt{Mandarin}、\texttt{XHC1983}、\texttt{HanyuPinyin}
% 的顺序选择最常用的拼音。\texttt{HanyuPinlu} 的质量较差，不采用。
%    \begin{macrocode}
function xpinyin.grep (pyt)
  if pyt.Mandarin then
    return pyt.Mandarin:match("%S+"), "Mandarin"
  elseif pyt.XHC1983 then
    return pyt.XHC1983:match(":(%S+)"), "XHC1983"
  elseif pyt.HanyuPinyin then
    return pyt.HanyuPinyin:match(":([^,%s]+)"), "HanyuPinyin"
  end
end
%    \end{macrocode}
%
% 根据 \texttt{XHC1983} 和 \texttt{HanyuPinyin} 选出多音字。
%    \begin{macrocode}
function xpinyin.multiple (pyt)
  if pyt.XHC1983 then
    local s = pyt.XHC1983:explode()
    if s[2] then
      local t = { }
      for i, v in ipairs(s) do
        t[#t + 1] = v:explode(":")[2]
      end
      return xpinyin.unique(t), "XHC1983"
    end
  elseif pyt.HanyuPinyin and pyt.HanyuPinyin:find("%D,") then
    local t = { }
    for _, v in ipairs(pyt.HanyuPinyin:explode()) do
      for _, py in ipairs(v:explode(":")[2]:explode(",")) do
        t[#t + 1] = py
      end
    end
    return xpinyin.unique(t), "HanyuPinyin"
  end
end
%    \end{macrocode}
%
% 删除掉数组中的重复元素。
%    \begin{macrocode}
function xpinyin.unique (t)
  local rt = xpinyin.remove_duplicate(t)
  if #rt > 1 then
    return table.concat(rt, ",")
  end
end
function xpinyin.remove_duplicate (t)
  local ht = { }
  local nt = { }
  for i, v in ipairs(t) do
    if not ht[v] then
      nt[#nt + 1] = v
      ht[v] = true
    end
  end
  return nt
end
%    \end{macrocode}
%
%    \begin{macrocode}
xpinyin.maketable()
%    \end{macrocode}
%
%    \begin{macrocode}
xpinyin.output()
%    \end{macrocode}
%
%    \begin{macrocode}
%</lua>
%    \end{macrocode}
%
% \end{implementation}
%
% \Finale
%
\endinput
